import { v4 as uuidv4 } from 'uuid';
import type { FlowNode, FlowEdge, JDFAttribute } from '@/models/jdf';
import type { ParseResult } from './types';
import { XMLParser, XMLValidator } from 'fast-xml-parser';

// Define the structure expected from fast-xml-parser
// Adjust based on parser options used
interface FastXMLNode {
    [key: string]: any; // Actual tag name
    _attributes?: { [key: string]: string };
    _text?: string;
    _children?: FastXMLNode[];
}

/**
 * Parses XML content using the fast-xml-parser library.
 * 
 * @param content The XML string content.
 * @returns A ParseResult object.
 */
export function parse(content: string): ParseResult {
    // 1. Validate first (optional but good practice)
    const validationResult = XMLValidator.validate(content, {
        allowBooleanAttributes: false // Example option
    });

    if (validationResult !== true && validationResult.err) {
        const { code, msg, line, col } = validationResult.err;
        return {
            success: false,
            error: `fast-xml-parser Error: ${code} - ${msg} (Line: ${line}, Col: ${col})`,
            nodes: [],
            edges: []
        };
    }

    // 2. Parse into JS object
    try {
        const parser = new XMLParser({
            ignoreAttributes: false,
            attributeNamePrefix: "", // No prefix for attributes
            attributesGroupName: "_attributes", // Group attributes under this key
            textNodeName: "_text", // Key for text content
            // Use an array for children regardless of number
            isArray: (name, jpath, isLeafNode, isAttribute) => !isAttribute,
             // Store children directly under tag name, e.g., { JDF: [ { JDFNode: [...] } ] }\n            // We need a way to handle multiple children with the same tag name, using arrays via isArray handles this\n             // Let's refine the structure slightly: always parse children into a '_children' array\n             // This requires post-processing the parsed object or a different parser config.\n            \n             // Alternative: Use a standard structure where tag names are keys, values are arrays of children\n             // Let's stick to a predictable structure using childrenPath\n             // childrenPath: '_children', // Doesn't seem to be a direct option, manual traversal needed\n            \n            // Let's try a simpler configuration first, and adapt traversal\n             allowBooleanAttributes: false,
             parseTagValue: false, // Keep values as strings
             parseAttributeValue: false,
             trimValues: true,
             commentPropName: "_comment",
             preserveOrder: false, // Order usually doesn't matter for JDF structure? If it does, set to true.
             processEntities: true,
             stopNodes: [], // No specific nodes to stop parsing at
        });

        const parsedObj = parser.parse(content);

        // 3. Extract root element (assuming only one top-level element)
        const rootTagName = Object.keys(parsedObj)[0];
        const rootNodeData = parsedObj[rootTagName];
        if (!rootTagName || !rootNodeData) {
            throw new Error('Could not find root element in parsed object.');
        }
        
        // Ensure rootNodeData is an array (due to isArray config)
        const actualRoot = Array.isArray(rootNodeData) ? rootNodeData[0] : rootNodeData;

        // 4. Convert JS object structure to Flow elements
        const { nodes, edges } = extractElementsFromJsObj(actualRoot, rootTagName);

        return {
            success: true,
            document: parsedObj, // Store the parsed object
            nodes,
            edges
        };

    } catch (err: any) {
        console.error('[FastXMLParserWrapper] Error:', err);
        // If parser throws, it might be a syntax error not caught by validator
         if (err.code && err.line && err.col) {
             return {
                success: false,
                error: `fast-xml-parser Error: ${err.code} - ${err.msg} (Line: ${err.line}, Col: ${err.col})`,
                nodes: [],
                edges: []
             };
         } else {
            return {
                success: false,
                error: err.message || 'An unexpected error occurred during fast-xml-parser processing.',
                nodes: [],
                edges: []
            };
         }
    }
}

// --- Helper functions specific to JS Object traversal --- 

/**
 * Extracts FlowNode and FlowEdge arrays by recursively traversing the 
 * JavaScript object structure generated by fast-xml-parser.
 * 
 * @param rootJsNode The root node object parsed by fast-xml-parser. 
 *                   Assumes the parser was configured with `isArray: () => true` 
 *                   so children are always arrays under their tag name keys.
 * @param rootTagName The tag name of the root node.
 * @returns An object containing the extracted nodes and edges arrays.
 */
function extractElementsFromJsObj(rootJsNode: any, rootTagName: string): { nodes: FlowNode[]; edges: FlowEdge[] } {
    const nodes: FlowNode[] = [];
    const edges: FlowEdge[] = [];

    // Start recursive processing
    processJsNode(rootJsNode, rootTagName, null, nodes, edges, 0);

    return { nodes, edges };
}

/**
 * Recursively processes a JS node object (from fast-xml-parser) 
 * to create corresponding FlowNode and FlowEdge objects.
 * 
 * @param jsNode The current JavaScript node object to process.\
 * @param nodeName The XML tag name for the current node.\
 * @param parentId The ID of the parent FlowNode (or null if this is the root).\
 * @param nodes The master array where created FlowNodes are collected.\
 * @param edges The master array where created FlowEdges are collected.\
 * @param level The current depth level in the XML tree (0 for root).\
 * @returns The unique ID generated for the newly created FlowNode.\
 */
function processJsNode(jsNode: any, nodeName: string, parentId: string | null, nodes: FlowNode[], edges: FlowEdge[], level: number): string {
    const nodeId = `node-${uuidv4()}`;

    // Extract attributes from the special `_attributes` property
    const attributes: JDFAttribute[] = [];
    if (jsNode._attributes) {
        for (const attrName in jsNode._attributes) {
            // Ensure attribute value is treated as a string
            attributes.push({ name: attrName, value: String(jsNode._attributes[attrName]) });
        }
    }

    // Extract text content from the special `_text` property
    const textContent = jsNode._text?.trim() || '';

    // Iterate over object keys to find children. Child nodes are expected
    // to be in arrays under their respective tag name keys, due to parser config.
    const childrenIds: string[] = [];
    let hasChildren = false;
    for (const key in jsNode) {
        // Ignore special properties used by the parser
        if (key === '_attributes' || key === '_text' || key === '_comment') continue;

        const children = jsNode[key];
        // Check if the value is an array (expected for child nodes)
        if (Array.isArray(children)) {
            children.forEach(childNode => {
                // Check if childNode is actually an object (nodes) or primitive (shouldn't happen?)
                if (typeof childNode === 'object' && childNode !== null) {
                    hasChildren = true;
                    // Recursively process the child node. The 'key' is its tag name.
                    const childId = processJsNode(childNode, key, nodeId, nodes, edges, level + 1);
                    childrenIds.push(childId);
                } else {
                    // Log unexpected structure - parser config might need adjustment
                    console.warn(`[FastXMLParserWrapper] Expected object for child under key '${key}', but got:`, childNode);
                }
            });
        }
    }

    // Create the FlowNode object using extracted data
    const flowNode: FlowNode = {
        id: nodeId,
        type: 'jdfNode', // Standard type for visualization
        position: { x: 0, y: 0 }, // Layout applied later
        data: {
            nodeName: nodeName, // Tag name passed from parent iteration
            attributes,
            textContent,
            hasChildren,
            level,
            isExpanded: true, // Default state
            childrenIds // Array of child FlowNode IDs
        },
        parentId: parentId ?? undefined, // Set parent ID if not root
        hidden: false // Default state
    };

    nodes.push(flowNode); // Add to the main nodes list

    // If this node has a parent, create an edge connecting them
    if (parentId) {
        const edgeId = `edge-${parentId}-${nodeId}`;
        const edge: FlowEdge = {
            id: edgeId,
            source: parentId,
            target: nodeId,
            type: 'smoothstep', // Vue Flow edge type
            hidden: false // Default state
        };
        edges.push(edge); // Add to the main edges list
    }

    return nodeId; // Return the ID for the parent to track
} 